using LS.GameKit.Behaviour;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.Events;

namespace LS.GameKit
{
    public abstract class PhysicsBehaviour : GameBehaviourMono
    {
        public UnityEvent<PhysicsBehaviourResult> OnEnterEvent = new UnityEvent<PhysicsBehaviourResult>();
        public UnityEvent<PhysicsBehaviourResult> OnUpdateEvent = new UnityEvent<PhysicsBehaviourResult>();
        public UnityEvent<PhysicsBehaviourResult> OnExitEvent = new UnityEvent<PhysicsBehaviourResult>();

        public virtual Transform CachedTransform => transform;

        public virtual Vector3 Position
        {
            get
            {
                return transform.position;
            }

            set
            {
                transform.position = value;
            }
        }

        public virtual Vector3 LocalPosition
        {
            get
            {
                return transform.localPosition;
            }
            set
            {
                transform.localPosition = value;
            }
        }

        public virtual Quaternion Rotation
        {
            get
            {
                return transform.rotation;
            }

            set
            {
                transform.rotation = value;
            }
        }

        public virtual Quaternion LocalRotation
        {
            get
            {
                return transform.localRotation;
            }

            set
            {
                transform.localRotation = value;
            }
        }

        public virtual void Translate(Vector3 delta)
        {
            transform.position += delta;
        }

        public virtual void TranslateLocal(Vector3 delta)
        {
            transform.localPosition += delta;
        }

        public virtual void Rotate(Quaternion delta)
        {
            transform.rotation *= delta;
        }

        public virtual void RotateLocal(Quaternion delta)
        {
            transform.localRotation *= delta;
        }

        public virtual void RotateEular(Vector3 delta)
        {
            Quaternion rotation = Quaternion.Euler(delta);
            transform.rotation *= rotation;
        }

        public virtual void RotateLocalEular(Vector3 delta)
        {
            Quaternion rotation = Quaternion.Euler(delta);
            transform.localRotation *= rotation;
        }

        public void DoEnter(PhysicsBehaviourResult result)
        {
            OnEnterEvent.Invoke(result);
        }

        public void DoUpdate(PhysicsBehaviourResult result)
        {
            OnUpdateEvent.Invoke(result);
        }

        public void DoExit(PhysicsBehaviourResult result)
        {
            OnExitEvent.Invoke(result);
        }

        public abstract bool ContainsCollider(Collider collider);
        public abstract bool ContainsCollider(Collider2D collider);
    }

    public abstract class PhysicsBehaviourResult
    {
        public abstract bool Is2D { get; }
        public abstract bool IsTrigger { get; }
        public abstract PhysicsBehaviour TargetBehaviour { get; }
    }

    public class Collision3DResult : PhysicsBehaviourResult
    {

        private PhysicsBehaviour m_PhysicsBehaviour = null;

        public Collider Collider { get; private set; }

        public Collision Collision { get; private set; }

        public override PhysicsBehaviour TargetBehaviour => m_PhysicsBehaviour;

        public override bool Is2D => false;

        public override bool IsTrigger => false;

        public Collision3DResult(Collision collision)
        {
            this.Collision = collision;
            this.m_PhysicsBehaviour = collision.collider.GetComponents<PhysicsBehaviour>().First(pb => pb.ContainsCollider(collision.collider));
        }
    }

    public class Collision2DResult : PhysicsBehaviourResult
    {

        private PhysicsBehaviour m_PhysicsBehaviour = null;

        public Collision2D Collision { get; private set; }

        public override PhysicsBehaviour TargetBehaviour => m_PhysicsBehaviour;

        public override bool Is2D => true;

        public override bool IsTrigger => false;

        public Collision2DResult(Collision2D collision)
        {
            this.Collision = collision;
            this.m_PhysicsBehaviour = collision.collider.GetComponents<PhysicsBehaviour>().First(pb => pb.ContainsCollider(collision.collider));
        }
    }

    public class Trigger3DInfo: PhysicsBehaviourResult
    {
        private PhysicsBehaviour m_PhysicsBehaviour = null;

        public Collider Collider { get; private set; }

        public override PhysicsBehaviour TargetBehaviour => m_PhysicsBehaviour;

        public override bool Is2D => true;

        public override bool IsTrigger => false;

        public Trigger3DInfo(Collider collider)
        {
            this.Collider = collider;
            this.m_PhysicsBehaviour = collider.GetComponents<PhysicsBehaviour>().First(pb => pb.ContainsCollider(collider));
        }
    }

    public class Trigger2DInfo : PhysicsBehaviourResult
    {
        private PhysicsBehaviour m_PhysicsBehaviour = null;

        public Collider2D Collider { get; private set; }

        public override PhysicsBehaviour TargetBehaviour => m_PhysicsBehaviour;

        public override bool Is2D => true;

        public override bool IsTrigger => false;

        public Trigger2DInfo(Collider2D collider)
        {
            this.Collider = collider;
            this.m_PhysicsBehaviour = collider.GetComponents<PhysicsBehaviour>().First(pb => pb.ContainsCollider(collider));
        }
    }
}
